package system.data;

import java.util.ArrayList;
import java.util.HashMap;

//<editor-fold defaultstate="collapsed" desc="Expression">
class Expression {
	private int exp_len;
	private char[] exp_array;
	private ArrayList<String> token_list;
	private boolean is_flushed;
	private char c;
	private StringBuffer sb;
	private int cur_token_index;
	private int token_count;

	public Expression(String expression) {
		this.exp_len = expression.length();
		this.exp_array = expression.toCharArray();
		this.token_list = new ArrayList<String>();
		this.is_flushed = true;
		this.cur_token_index = -1;
		this.sb = new StringBuffer();
		this.initTokenList();
		this.token_count = this.token_list.size();

	}

	private void flush() {
		if (this.is_flushed)
			return;

		this.token_list.add(this.sb.toString());
		this.sb = new StringBuffer();
		this.is_flushed = true;
	}

	private void collect() {
		this.sb.append(c);
		this.is_flushed = false;
	}

	private boolean nextToken() {

		if (this.cur_token_index < this.token_count - 1) {
			this.cur_token_index++;
			return true;
		}

		return false;
	}

	private String currentToken() {
		return this.token_list.get(this.cur_token_index);
	}

	private void initTokenList() {
		for (int i = 0; i < this.exp_len; i++) {
			c = exp_array[i];
			if (Character.isWhitespace(c)) {
				this.flush();
				continue;
			}

			switch (c) {
			case '<':

				this.flush();
				if (i + 1 < this.exp_len && exp_array[i + 1] == '=') {
					this.token_list.add("<=");
					i++;
				} else {
					this.token_list.add("<");
				}
				break;

			case '>':

				this.flush();
				if (i + 1 < this.exp_len && exp_array[i + 1] == '=') {
					this.token_list.add(">=");
					i++;
				} else {
					this.token_list.add(">");
				}
				break;

			case '(':

				this.flush();
				this.token_list.add("(");
				break;

			case ')':

				this.flush();
				this.token_list.add(")");
				break;

			case '=':

				if (i + 1 < this.exp_len && exp_array[i + 1] == '=') {
					this.flush();
					this.token_list.add("==");
					i++;
				} else {
					this.collect();
				}
				break;

			case '!':

				if (i + 1 < this.exp_len && exp_array[i + 1] == '=') {
					this.flush();
					this.token_list.add("!=");
					i++;
				} else {
					this.collect();
				}
				break;

			default:

				this.collect();
				break;
			}
		}

		this.flush();
	}

	public boolean eval(DataRow dr) {
		this.nextToken();
		boolean result = this.doAndOr(dr);
		this.cur_token_index = -1;
		return result;
	}

	private boolean doAndOr(DataRow dr) {
		boolean result = this.doNot(dr);
		String op = "";

		boolean result_right;

		while ((op = this.currentToken()).equals("and") || op.equals("or")) {
			this.nextToken();

			if (op.equals("and")) {
				result_right = this.doNot(dr);
				result = result && result_right;
			} else {
				result_right = this.doNot(dr);
				result = result || result_right;
			}
		}

		return result;
	}

	private boolean doNot(DataRow dr) {
		String op = "";

		if ((op = this.currentToken()).equals("not"))
			this.nextToken();

		boolean result = this.doBrackets(dr);

		if (op.equals("not"))
			return !result;

		return result;
	}

	private boolean doBrackets(DataRow dr) {
		boolean result;
		if (this.currentToken().equals("(")) {
			this.nextToken();
			result = this.doAndOr(dr);
			this.nextToken();
		} else {
			result = this.doCompare(dr);
		}

		return result;
	}

	private boolean doCompare(DataRow dr) {
		Object field = dr.get(this.currentToken());
		this.nextToken();
		String opt = this.currentToken();
		this.nextToken();
		String value = this.currentToken();
		this.nextToken();

		if (opt.equals("like")) {
			return isLike(field, value);
		} else if (opt.equals(">")) {
			return isGreat(field, value);
		} else if (opt.equals("<")) {
			return isLess(field, value);
		} else if (opt.equals("==")) {
			return isEquals(field, value);
		} else if (opt.equals(">=")) {
			return isGreatEquals(field, value);
		} else if (opt.equals("<=")) {
			return isLessEquals(field, value);
		} else if (opt.equals("!=")) {
			return isNotEquals(field, value);
		}

		return false;

	}

	// <editor-fold defaultstate="collapsed" desc="tool method">

	private static boolean isLike(Object field, String value) {
		int len = value.length();
		if (value.startsWith("'%") && value.endsWith("%'"))
			return Convert.toString(field)
					.contains(value.substring(2, len - 2));
		else if (value.startsWith("'%"))
			return Convert.toString(field)
					.endsWith(value.substring(2, len - 1));
		else if (value.endsWith("%'"))
			return Convert.toString(field).startsWith(
					value.substring(1, len - 2));
		else
			return Convert.toString(field).equals(value.substring(1, len - 1));
	}

	private static boolean isLess(Object field, String value) {
		if (field instanceof Number)
			return Convert.toFloat(field) < Convert.toFloat(value);
		return Convert.toString(field).compareTo(
				value.substring(1, value.length() - 1)) < 0;
	}

	private static boolean isGreat(Object field, String value) {
		if (field instanceof Number)
			return Convert.toFloat(field) > Convert.toFloat(value);
		return Convert.toString(field).compareTo(
				value.substring(1, value.length() - 1)) > 0;
	}

	private static boolean isEquals(Object field, String value) {
		if (value.equals("null"))
			return field == null;

		if (field instanceof Number)
			return Convert.toFloat(field) == Convert.toFloat(value);
		if (field instanceof Boolean)
			return Convert.toBool(field) == Convert.toBool(value);

		return Convert.toString(field).equals(
				value.substring(1, value.length() - 1));
	}

	private static boolean isNotEquals(Object field, String value) {
		if (value.equals("null"))
			return field != null;

		if (field instanceof Number)
			return Convert.toFloat(field) != Convert.toFloat(value);
		if (field instanceof Boolean)
			return Convert.toBool(field) == Convert.toBool(value);

		return !Convert.toString(field).equals(
				value.substring(1, value.length() - 1));
	}

	private static boolean isLessEquals(Object field, String value) {
		if (field instanceof Number)
			return Convert.toFloat(field) <= Convert.toFloat(value);
		return Convert.toString(field).compareTo(
				value.substring(1, value.length() - 1)) <= 0;
	}

	private static boolean isGreatEquals(Object field, String value) {
		if (field instanceof Number)
			return Convert.toFloat(field) >= Convert.toFloat(value);
		return Convert.toString(field).compareTo(
				value.substring(1, value.length() - 1)) >= 0;
	}

	// </editor-fold>

	private static HashMap<String, Expression> expression_map = new HashMap<String, Expression>();

	public static Expression FindExpression(String expression) {
		Expression e = expression_map.get(expression);
		if (e == null) {
			e = new Expression(expression);
			expression_map.put(expression, e);
		}
		return e;
	}
}
// </editor-fold>